// (C) Copyright 2012 Kystar. All rights reserved.

`timescale 1ns/100ps
`default_nettype none

module mcu_decode
(
    input  wire        I_rst_n,
    input  wire        I_sclk,

    // from mcu_rx
    input  wire        I_clk_posedge,
    input  wire        I_dat_begin,
    input  wire        I_datin_valid,
    input  wire [7:0]  I_datin,

    // to mcu_tx
    output reg         O_datout_valid,
    output reg  [7:0]  O_datout,

    //--------------------------------------------------------------------
    // write
    //--------------------------------------------------------------------
    output wire          O_action_wren,
    output wire          O_cfg_wren,
    output wire          O_edid_ram_wren,
    output wire [  3: 0] O_px_pkg_ram_wren,
    output reg  [ 11: 0] O_waddr,
    output wire [  7: 0] O_wdata,

    //--------------------------------------------------------------------
    // read 
    //--------------------------------------------------------------------
    output reg  [ 11: 0] O_raddr,
    //
    output wire          O_status_rden,
    input  wire [  7: 0] I_status_rdata,
    //
    output wire          O_action_rden,
    input  wire [  7: 0] I_action_rdata,
    //
    output wire          O_cfg_rden,
    input  wire [  7: 0] I_cfg_rdata,
    //
    output wire          O_edid_ram_rden,
    input  wire [  7: 0] I_edid_ram_rdata,
    //
    output wire [  3: 0] O_px_pkg_ram_rden,
    input  wire [  7: 0] I_p0_pkg_ram_rdata,
    input  wire [  7: 0] I_p1_pkg_ram_rdata,
    input  wire [  7: 0] I_p2_pkg_ram_rdata,
    input  wire [  7: 0] I_p3_pkg_ram_rdata,
    //
    output wire [  3: 0] O_px_comm_back_ram_rden,
    input  wire [  7: 0] I_p0_comm_back_ram_rdata,
    input  wire [  7: 0] I_p1_comm_back_ram_rdata,
    input  wire [  7: 0] I_p2_comm_back_ram_rdata,
    input  wire [  7: 0] I_p3_comm_back_ram_rdata

);

/******************************************************************************
                                <localparams>
******************************************************************************/
localparam MCU_RD = 8'hAA;
localparam MCU_WR = 8'hE5;

localparam [63:0] IDLE      = "idle";
localparam [63:0] RX_TYPE   = "rx_type";
localparam [63:0] RX_ADDR   = "rx_addr";
localparam [63:0] RX_SIZE   = "rx_size";
localparam [63:0] RX_DATA   = "rx_data";
localparam [63:0] TX_DATA   = "tx_data";
localparam [63:0] RX_END    = "rx_end";
localparam [63:0] TX_END    = "tx_end";
localparam [63:0] WAIT_DAT  = "wait_dat";

localparam STATUS_REG           = 24'hAA_0000;
localparam ACTION_REG           = 24'hAA_1000;
localparam CFG_REG              = 24'hAB_0000;
localparam EDID_REG             = 24'hAB_9000;

localparam P0_PKG_RAM_REG       = 24'hAB_1000;
localparam P0_COMM_BACK_RAM_REG = 24'hAB_2000;

localparam P1_PKG_RAM_REG       = 24'hAB_3000;
localparam P1_COMM_BACK_RAM_REG = 24'hAB_4000;

localparam P2_PKG_RAM_REG       = 24'hAB_5000;
localparam P2_COMM_BACK_RAM_REG = 24'hAB_6000;

localparam P3_PKG_RAM_REG       = 24'hAB_7000;
localparam P3_COMM_BACK_RAM_REG = 24'hAB_8000;

localparam PX_PKG_RAM_REG       = 24'hAC_0000;

/******************************************************************************
                              <internal signals>
******************************************************************************/
reg  [63:0] ct_state;
reg  [63:0] nt_state;
reg  [11:0] byte_cnt;
reg         byte_add;
reg         byte_clr;
reg  [7:0]  op_type;
reg  [23:0] op_addr;
reg  [15:0] op_size;
reg         addr_add;
reg         load_type;
reg  [2:0]  load_addr;
reg  [1:0]  load_size;
reg         rx_en;
reg         tx_en;
reg         rd_en;
reg  [15:0] rcv_bit_cnt;
reg  [2:0]  valid_bit_cnt;
wire [11:0]  rddat_sel;
wire [11:0]  rden;
wire [6:0]  wren;

/******************************************************************************
                                <module body>
******************************************************************************/
//--------------------------------------------------------------------
// write
//--------------------------------------------------------------------
assign O_action_wren = rx_en && (op_addr[23:12] == ACTION_REG[23:12]);
assign O_cfg_wren = rx_en && (op_addr[23:12] == CFG_REG[23:12]);
assign O_edid_ram_wren = rx_en && (op_addr[23:12] == EDID_REG[23:12]);
assign O_px_pkg_ram_wren[0] = rx_en && (op_addr[23:12] == P0_PKG_RAM_REG[23:12] || op_addr[23:12] == PX_PKG_RAM_REG[23:12]);
assign O_px_pkg_ram_wren[1] = rx_en && (op_addr[23:12] == P1_PKG_RAM_REG[23:12] || op_addr[23:12] == PX_PKG_RAM_REG[23:12]);
assign O_px_pkg_ram_wren[2] = rx_en && (op_addr[23:12] == P2_PKG_RAM_REG[23:12] || op_addr[23:12] == PX_PKG_RAM_REG[23:12]);
assign O_px_pkg_ram_wren[3] = rx_en && (op_addr[23:12] == P3_PKG_RAM_REG[23:12] || op_addr[23:12] == PX_PKG_RAM_REG[23:12]);

assign wren = {O_edid_ram_wren,
               O_action_wren,
               O_cfg_wren,
               O_px_pkg_ram_wren[3:0]};

always @(posedge I_sclk or negedge I_rst_n)
    if(!I_rst_n)
        addr_add <= 1'b0;
    else
        addr_add <= wren != 'd0;

assign O_wdata = I_datin;

always @(posedge I_sclk or negedge I_rst_n)
    if(!I_rst_n)
        O_waddr <= 12'd0;
    else if(load_size[0])
        O_waddr <= op_addr[11:0];
    else if(addr_add)
        O_waddr <= O_waddr + 1'b1;

//--------------------------------------------------------------------
// read
//--------------------------------------------------------------------
assign O_status_rden = tx_en && (op_addr[23:12] == STATUS_REG[23:12]);
assign O_action_rden = tx_en && (op_addr[23:12] == ACTION_REG[23:12]);
assign O_cfg_rden = tx_en && (op_addr[23:12] == CFG_REG[23:12]);
assign O_edid_ram_rden = tx_en && (op_addr[23:8] == EDID_REG[23:8]);
assign O_px_pkg_ram_rden[0] = tx_en && (op_addr[23:12] == P0_PKG_RAM_REG[23:12]);
assign O_px_pkg_ram_rden[1] = tx_en && (op_addr[23:12] == P1_PKG_RAM_REG[23:12]);
assign O_px_pkg_ram_rden[2] = tx_en && (op_addr[23:12] == P2_PKG_RAM_REG[23:12]);
assign O_px_pkg_ram_rden[3] = tx_en && (op_addr[23:12] == P3_PKG_RAM_REG[23:12]);
assign O_px_comm_back_ram_rden[0] = tx_en && (op_addr[23:12] == P0_COMM_BACK_RAM_REG[23:12]);
assign O_px_comm_back_ram_rden[1] = tx_en && (op_addr[23:12] == P1_COMM_BACK_RAM_REG[23:12]);
assign O_px_comm_back_ram_rden[2] = tx_en && (op_addr[23:12] == P2_COMM_BACK_RAM_REG[23:12]);
assign O_px_comm_back_ram_rden[3] = tx_en && (op_addr[23:12] == P3_COMM_BACK_RAM_REG[23:12]);

assign rden = {O_edid_ram_rden,O_px_comm_back_ram_rden[3:0],O_px_pkg_ram_rden[3:0],O_cfg_rden,O_action_rden,O_status_rden};

assign rddat_sel[0]  = (op_addr[23:12] == STATUS_REG[23:12]);
assign rddat_sel[1]  = (op_addr[23:12] == ACTION_REG[23:12]);
assign rddat_sel[2]  = (op_addr[23:12] == CFG_REG[23:12]);
assign rddat_sel[3]  = (op_addr[23:12] == P0_PKG_RAM_REG[23:12]);
assign rddat_sel[4]  = (op_addr[23:12] == P1_PKG_RAM_REG[23:12]);
assign rddat_sel[5]  = (op_addr[23:12] == P2_PKG_RAM_REG[23:12]);
assign rddat_sel[6]  = (op_addr[23:12] == P3_PKG_RAM_REG[23:12]);
assign rddat_sel[7]  = (op_addr[23:12] == P0_COMM_BACK_RAM_REG[23:12]);
assign rddat_sel[8]  = (op_addr[23:12] == P1_COMM_BACK_RAM_REG[23:12]);
assign rddat_sel[9]  = (op_addr[23:12] == P2_COMM_BACK_RAM_REG[23:12]);
assign rddat_sel[10] = (op_addr[23:12] == P3_COMM_BACK_RAM_REG[23:12]);
assign rddat_sel[11] = (op_addr[23:12] == EDID_REG[23:12]);

always @(*)
    case(1'b1)
        rddat_sel[0]  : O_datout = I_status_rdata;
        rddat_sel[1]  : O_datout = I_action_rdata;
        rddat_sel[2]  : O_datout = I_cfg_rdata;
        rddat_sel[3]  : O_datout = I_p0_pkg_ram_rdata;
        rddat_sel[4]  : O_datout = I_p1_pkg_ram_rdata;
        rddat_sel[5]  : O_datout = I_p2_pkg_ram_rdata;
        rddat_sel[6]  : O_datout = I_p3_pkg_ram_rdata;
        rddat_sel[7]  : O_datout = I_p0_comm_back_ram_rdata;
        rddat_sel[8]  : O_datout = I_p1_comm_back_ram_rdata;
        rddat_sel[9]  : O_datout = I_p2_comm_back_ram_rdata;
        rddat_sel[10] : O_datout = I_p3_comm_back_ram_rdata;
        rddat_sel[11] : O_datout = I_edid_ram_rdata;
        default      : O_datout = 8'h00;
    endcase

always @(posedge I_sclk or negedge I_rst_n)
    if (!I_rst_n)
        O_raddr <= 'd0;
    else if (load_size[0])
        O_raddr <= op_addr[11:0];
    else if (rden != 'd0)
        O_raddr <= O_raddr + 1'b1;

//--------------------------------------------------------------------
// state
//--------------------------------------------------------------------
always @(posedge I_sclk or negedge I_rst_n) begin
    if(!I_rst_n)
        ct_state <= WAIT_DAT;
    else if(I_dat_begin)
        ct_state <= IDLE;
    else
        ct_state <= nt_state;
    end

always @( * ) begin
    nt_state = ct_state;

    byte_add = 1'b0;
    byte_clr = 1'b0;
    rx_en = 1'b0;

    load_type = 1'b0;
    load_addr = 3'b000;
    load_size = 2'b00;

    case(ct_state)
        WAIT_DAT : begin
            nt_state = WAIT_DAT;
            end

        IDLE : begin
            nt_state = RX_TYPE;
            byte_clr = 1'b1;
            end

        RX_TYPE : begin
            if(I_datin_valid) begin
                load_type = 1'b1;
                if((I_datin == MCU_RD) || (I_datin == MCU_WR))
                    nt_state = RX_ADDR;
                else
                    nt_state = WAIT_DAT;
                end
            end

        RX_ADDR : begin
            if(I_datin_valid) begin
                load_addr[2] = (byte_cnt[1:0] == 2'd0);
                load_addr[1] = (byte_cnt[1:0] == 2'd1);
                load_addr[0] = (byte_cnt[1:0] == 2'd2);
                if(byte_cnt[1]) begin
                    nt_state = RX_SIZE;
                    byte_clr = 1'b1;
                    end
                else
                    byte_add = 1'b1;
                end
            end

        RX_SIZE : begin
            if(I_datin_valid) begin
                load_size[1] = (byte_cnt[1:0] == 2'd0);
                load_size[0] = (byte_cnt[1:0] == 2'd1);
                if(byte_cnt[1:0] >= 2'd3) begin
                    byte_clr = 1'b1;
                    if(op_type == MCU_WR)
                        nt_state = RX_DATA;
                    else
                        nt_state = TX_DATA;                          
                    end
                else
                    byte_add = 1'b1;
                end
            end

        RX_DATA : begin
            if(I_datin_valid) begin
                rx_en = 1'b1;
                if(byte_cnt + 1'b1 >= op_size) begin
                    nt_state = RX_END;
                    byte_clr = 1'b1;
                    end
                else
                    byte_add = 1'b1;
                end
            end

        RX_END : begin
            nt_state = WAIT_DAT;
            end

        TX_DATA : begin
            if(I_datin_valid) begin
                if(byte_cnt + 1'b1 >= op_size) begin
                    nt_state = TX_END;
                    byte_clr = 1'b1;
                    end
                else
                    byte_add = 1'b1;
                end
            end

        TX_END : begin
            nt_state = WAIT_DAT;
            end

        default : begin
            nt_state = WAIT_DAT;
            end

    endcase
    end

always @(posedge I_sclk or negedge I_rst_n)
    if(!I_rst_n)
        byte_cnt <= 12'd0;
    else
        begin
        if(byte_clr || I_dat_begin)
            byte_cnt <= 12'd0;
        else if(byte_add)
            byte_cnt <= byte_cnt + 1'b1;
        end

always @(posedge I_sclk or negedge I_rst_n)
    if(!I_rst_n)
        op_type <= 8'd0;
    else if(load_type)
        op_type <= I_datin;

always @(posedge I_sclk or negedge I_rst_n)
    if(!I_rst_n)
        op_addr <= 24'd0;
    else if(load_addr[2])
        op_addr[23:16] <= I_datin;
    else if(load_addr[1])
        op_addr[15:8] <= I_datin;
    else if(load_addr[0])
        op_addr[7:0] <= I_datin;

always @(posedge I_sclk or negedge I_rst_n)
    if(!I_rst_n)
        op_size <= 16'd0;
    else if(load_size[1])
        op_size[15:8] <= I_datin;
    else if(load_size[0])
        op_size[7:0] <= I_datin;

always @(posedge I_sclk or negedge I_rst_n) begin
    if(!I_rst_n) begin
        rd_en <= 1'b0;
        valid_bit_cnt <= 3'b000;
        end
    else if(I_dat_begin) begin
        rd_en <= 1'b0;
        valid_bit_cnt <= 3'b000;
        end
    else if(op_type == MCU_RD) begin
        rd_en <= (rcv_bit_cnt >= 16'd63);
        if(rd_en && I_clk_posedge)
            valid_bit_cnt <= valid_bit_cnt + 1'b1;
        end
    end

always @(posedge I_sclk or negedge I_rst_n)
    if(!I_rst_n)
        rcv_bit_cnt <= 16'd0;
    else if(I_dat_begin)
        rcv_bit_cnt <= 16'd0;
    else if(I_clk_posedge)
        rcv_bit_cnt <= rcv_bit_cnt + 1'b1;

always @(posedge I_sclk or negedge I_rst_n)
    if(!I_rst_n)
        begin
        tx_en <= 1'b0;
        O_datout_valid <= 1'b0;
        end
    else
        begin
        tx_en <= rd_en && I_clk_posedge && (valid_bit_cnt == 3'b000);
        O_datout_valid <= rden != 'd0;
        end

endmodule
`default_nettype wire
